package com.grace.cms.general.config;

import com.grace.common.enums.GraceServerRoot;
import com.grace.common.enums.GraceStorageRoot;
import com.grace.common.utils.StringUtils;
import com.grace.common.utils.file.FileUtils;
import com.grace.cms.common.constant.OlySystemConstant;
import com.grace.cms.general.interceptor.IPInterceptor;
import com.grace.cms.general.interceptor.ThemeInterceptor;
import jakarta.servlet.http.HttpServletRequest;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.nio.file.AccessDeniedException;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.List;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.ClassPathResource;
import org.springframework.core.io.FileSystemResource;
import org.springframework.core.io.Resource;
import org.springframework.lang.Nullable;
import org.springframework.web.servlet.config.annotation.InterceptorRegistry;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
import org.springframework.web.servlet.resource.AbstractResourceResolver;
import org.springframework.web.servlet.resource.EncodedResourceResolver;
import org.springframework.web.servlet.resource.ResourceResolverChain;

@Configuration
public class WebConfig implements WebMvcConfigurer {

        @Autowired
        private IPInterceptor ipInterceptor;

        @Autowired
        private ThemeInterceptor themeInterceptor;

        @Override
        public void addResourceHandlers(ResourceHandlerRegistry registry) {
                
                /**
                 * 主题静态资源配置 访问路径：/themeName/static/css/style.css-> 本地../themes下
                 * /themeName/static/css/style.css classpath /static下 /css/style.css
                 * 
                 * /nice/static/css/style.css
                 * 
                 * /themes/css/style.css /static/css/style.css /
                 */

                registry.addResourceHandler("/{themeName}/static/{*resourcePaths}").resourceChain(true)
                                .addResolver(new EncodedResourceResolver())
                                .addResolver(new ThemePathResourceResolver(
                                                Paths.get(GraceServerRoot.THEME_DIR.getWorkRoot()), "static/",
                                                "/static/", true));
                /** 文件资源映射 */
                registry.addResourceHandler("/{themeName}/storage/{*resourcePaths}").resourceChain(true)
                                .addResolver(new EncodedResourceResolver())
                                .addResolver(new ThemePathResourceResolver(
                                                Paths.get(GraceStorageRoot.UPLOAD_DIR.getWorkRoot()), null, "/storage/",
                                                false));
                registry.addResourceHandler("/{themeName}/storage/avatar/{*resourcePaths}").resourceChain(true)
                                .addResolver(new EncodedResourceResolver())
                                .addResolver(new ThemePathResourceResolver(
                                                Paths.get(GraceStorageRoot.AVATAR_DIR.getWorkRoot()), null,
                                                "/storage/avatar/", false));
        }

        /**
         * 自定义拦截规则
         */
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
                // 拦截非指定IP对本地文件的操作
                registry.addInterceptor(ipInterceptor)
                                .addPathPatterns("/*" + OlySystemConstant.MANAGE_URL_PREFIX + "/**");

                // 拦截主题请求
                registry.addInterceptor(themeInterceptor).addPathPatterns("/**").excludePathPatterns(
                                "/*" + OlySystemConstant.MANAGE_URL_PREFIX + "/**", "/*/hand/**", "/*/static/**",
                                "/*/storage/**",  "/error");
        }

        private static class ThemePathResourceResolver extends AbstractResourceResolver {

                private final Path themeRoot;

                private final String localPath;

                private final String splix;

                private final boolean byThemeName;

                private ThemePathResourceResolver(Path themeRoot, String localPath, String splix, boolean byThemeName) {
                        this.themeRoot = themeRoot;
                        this.localPath = localPath;
                        this.splix = splix;
                        this.byThemeName = byThemeName;
                }

                @Override
                protected Resource resolveResourceInternal(@Nullable HttpServletRequest request,
                                @Nullable String requestPath, List<? extends Resource> locations,
                                ResourceResolverChain chain) {
                        // 手动解析路径变量
                        String[] pathParts = requestPath.split("/");
                        if (pathParts.length < 3) {
                                return null; // 请求路径格式不符合预期
                        }
                        String themeName = pathParts[0];
                        String resourcePaths = URLDecoder.decode(
                                        requestPath.substring(requestPath.indexOf(splix) + splix.length()),
                                        StandardCharsets.UTF_8);

                        // 检查 themeName 和 resourcePaths 是否为空
                        if (StringUtils.isAnyBlank(themeName, resourcePaths)) {
                                return null; // 返回 null 表示无法解析资源
                        }
                        Path resourcePath;
                        // 构造资源的完整路径
                        if (byThemeName) {
                                resourcePath = themeRoot.resolve(themeName + splix + resourcePaths);
                        } else {
                                resourcePath = themeRoot.resolve(resourcePaths);
                        }
                        // 防止目录遍历攻击
                        try {
                                FileUtils.checkDirectoryTraversal(themeRoot, resourcePath);
                        } catch (AccessDeniedException e) {
                                return null;
                        }

                        // 创建资源对象并检查是否可读
                        Resource location = new FileSystemResource(resourcePath);
                        if (!location.exists() || !location.isReadable()) {
                                if (localPath != null) {
                                        Resource classpathResource = new ClassPathResource(localPath + resourcePaths);
                                        if (classpathResource.exists() && classpathResource.isReadable()) {
                                                return classpathResource; // 如果 classpath 资源存在且可读，返回
                                        }
                                }

                                return null;
                        }
                        return location; // 返回解析成功的资源
                }

                @Override
                protected String resolveUrlPathInternal(String resourcePath, List<? extends Resource> locations,
                                ResourceResolverChain chain) {
                        // 不需要实现此方法，因为静态资源路径由 Spring 自动处理
                        return null;
                }
        }
}